package task

import (
	"fmt"
	"pids-cloud-server/constants"
	"pids-cloud-server/entity"
	"pids-cloud-server/logging"
	"time"

	"pids-cloud-server/utils"

	"github.com/google/uuid"
	"gorm.io/driver/mysql"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
	"gorm.io/gorm/clause"
)

func DbTask() {
	logging.Info("---------定时采集数据库数据任务开始---------")
	start := time.Now()

	// 获取数据库连接
	db := utils.GetDB()
	if db == nil {
		logging.Error("数据库连接失败，跳过数据库采集任务")
		return
	}

	var alarminfos []entity.AlarmInfo
	if tx := db.Where("status = ? AND type = ?", entity.ACTIVE, entity.DATABASE_ERROR).Find(&alarminfos); tx.Error == nil {
		for _, alarmInfo := range alarminfos {
			constants.DATABASE_ALARM_INFO_MAP.Store(alarmInfo.HostIP, alarmInfo)
		}
	} else {
		logging.Error("初始化数据库告警数据失败,退出服务！", tx.Error)
		panic(tx.Error)
	}
	var dbInfos []entity.DbInfo
	db.Model(&entity.DbInfo{}).Find(&dbInfos)
	if dbInfos == nil {
		logging.Info("数据库信息为空，退出服务！")
		return
	}
	var allAlarmInfo []entity.AlarmInfo
	for i := range dbInfos {
		dbInfo := &dbInfos[i]
		var alarmInfo *entity.AlarmInfo
		var err error
		switch dbInfo.DbType {
		case "mysql":
			err = collectMysql(dbInfo)
		case "postgresql":
			err = collectPg(dbInfo)
		case "sqlserver":
			err = collectSqlServer(dbInfo)
		}
		// 如果发生错误，生成告警信息；否则恢复告警信息
		if err != nil {
			alarmInfo = generateDBAlarmInfo(dbInfo)
		} else {
			alarmInfo = recoverDBAlarmInfo(dbInfo)
		}
		// 将告警信息添加到列表中
		if alarmInfo != nil {
			allAlarmInfo = append(allAlarmInfo, *alarmInfo)
		}
	}
	//保存、更新告警数据
	if len(allAlarmInfo) > 0 {
		tx := db.Clauses(clause.OnConflict{
			Columns:   []clause.Column{{Name: "id"}},
			DoUpdates: clause.AssignmentColumns([]string{"status", "modify_time", "level_id", "detail"}),
		}).CreateInBatches(&allAlarmInfo, 100)
		if tx.Error != nil {
			logging.Error("保存REDIS告警数据失败：" + tx.Error.Error())
		}
	}
	db.Save(&dbInfos)
	logging.Info("定时采集数据库数据任务结束---------耗时：", time.Since(start))
}

func recoverDBAlarmInfo(dbInfo *entity.DbInfo) (alarmInfo *entity.AlarmInfo) {
	value, exists := constants.DATABASE_ALARM_INFO_MAP.Load(dbInfo.IP)
	//存在旧告警，且没有产生新告警时
	if exists {
		// Redis 变正常，删除告警，并生成恢复告警
		logging.Infof("数据库 %s:%s 恢复正常, 清除告警", dbInfo.IP, dbInfo.AliasName)
		if alarmValue, ok := value.(entity.AlarmInfo); ok {
			alarmInfo = &alarmValue // 取地址
		} else {
			return nil // 避免 panic
		}
		alarmInfo.Status = entity.INACTIVE
		alarmInfo.ModifyTime = time.Now()
		constants.DATABASE_ALARM_INFO_MAP.Delete(dbInfo.IP)
	}
	return alarmInfo
}

func generateDBAlarmInfo(dbInfo *entity.DbInfo) (alarmInfo *entity.AlarmInfo) {
	value, exsits := constants.DATABASE_ALARM_INFO_MAP.Load(dbInfo.IP)
	if exsits {
		// 当前数据库实例存在告警进来，则更新告警记录
		if alarmValue, ok := value.(entity.AlarmInfo); ok {
			alarmInfo = &alarmValue
		} else {
			alarmInfo = nil
		}
		alarmInfo.Detail = fmt.Sprintf("数据库（%s）%s:%s异常;", dbInfo.AliasName, dbInfo.IP, dbInfo.Port)
		alarmInfo.ModifyTime = time.Now()
		constants.DATABASE_ALARM_INFO_MAP.Store(dbInfo.IP, alarmInfo)
	} else {
		// 当前数据库实例不存在告警进来，则新增告警记录

		alarmInfo = &entity.AlarmInfo{
			ID:         uuid.NewString(),
			Type:       entity.DATABASE_ERROR,
			Detail:     fmt.Sprintf("数据库(%s)%s:%s异常;", dbInfo.AliasName, dbInfo.IP, dbInfo.Port),
			HostIP:     dbInfo.IP,
			Flag:       entity.Server,
			Status:     entity.ACTIVE,
			LevelID:    1,
			CreateTime: time.Now(),
			ModifyTime: time.Now(),
		}
		constants.DATABASE_ALARM_INFO_MAP.Store(dbInfo.IP, alarmInfo)
	}
	return alarmInfo
}

func collectPg(dbInfo *entity.DbInfo) (err error) {
	// 构建 PostgreSQL DSN
	dsn := fmt.Sprintf("host=%s port=%s user=%s password=%s dbname=%s sslmode=disable",
		dbInfo.IP, dbInfo.Port, dbInfo.User, dbInfo.Password, dbInfo.DbName)

	// 打开数据库连接
	db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
	dbInfo.UpdateTime = time.Now()
	dbInfo.DbState = entity.ABNORMAL // 默认设置为异常状态
	if err != nil {
		return err
	}

	// 获取底层数据库连接
	sqlDb, err := db.DB()
	if err != nil {
		return err
	}
	defer sqlDb.Close() // 确保资源释放
	// 检查数据库连接状态
	if err := sqlDb.Ping(); err != nil {
		return err
	}
	connections, maxConnections, err := getPostgresConnections(db)
	dbInfo.ConnectionCount = connections
	dbInfo.MaxConnectionCount = maxConnections
	//采集数据库大小
	databaseSize, err := getPostgresDatabaseSize(db, dbInfo.DbName)
	dbInfo.DatabaseSize = databaseSize
	dbInfo.DbState = entity.NORMAL
	return err
}
func getPostgresConnections(db *gorm.DB) (currentConnections int, maxConnections int, err error) {
	// 查询当前连接数
	err = db.Raw("SELECT COUNT(*) FROM pg_stat_activity").Scan(&currentConnections).Error
	if err != nil {
		return 0, 0, err
	}

	// 查询最大连接数
	err = db.Raw("SHOW max_connections").Scan(&maxConnections).Error
	if err != nil {
		return 0, 0, err
	}

	return currentConnections, maxConnections, nil
}
func getPostgresDatabaseSize(db *gorm.DB, dbName string) (size string, err error) {
	query := `
        SELECT pg_size_pretty(pg_database_size(?)) AS db_size
    `
	err = db.Raw(query, dbName).Scan(&size).Error
	return size, err
}

// 采集sqlserver数据库信息
func collectSqlServer(dbInfo *entity.DbInfo) (err error) {
	// todo
	return nil
}

// 获取sqlserver数据库连接数
func getSQLServerConnections(db *gorm.DB) (currentConnections int, maxConnections int, err error) {
	// 查询当前连接数
	err = db.Raw("SELECT COUNT(*) FROM sys.dm_exec_sessions WHERE is_user_process = 1").Scan(&currentConnections).Error
	if err != nil {
		return 0, 0, err
	}

	// 查询最大连接数
	err = db.Raw("SELECT value_in_use FROM sys.configurations WHERE name = 'user connections'").Scan(&maxConnections).Error
	if err != nil {
		return 0, 0, err
	}

	return currentConnections, maxConnections, nil
}

func collectMysql(dbInfo *entity.DbInfo) (err error) {
	dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",
		dbInfo.User, dbInfo.Password, dbInfo.IP, dbInfo.Port, dbInfo.DbName)
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
	dbInfo.UpdateTime = time.Now()
	dbInfo.DbState = entity.ABNORMAL
	if err != nil {
		return err
	}
	sqlDb, err := db.DB()
	if err != nil {
		return err
	}
	defer sqlDb.Close() // 确保资源释放
	// 检查数据库连接状态
	if err := sqlDb.Ping(); err != nil {
		return err
	}
	//采集当前连接数和最大连接数
	connections, maxConnections, err := getMySQLConnections(db)
	dbInfo.ConnectionCount = connections
	dbInfo.MaxConnectionCount = maxConnections
	dbInfo.DbState = entity.NORMAL
	//采集数据库大小
	databaseSize, err := getMySQLDatabaseSize(db, dbInfo.DbName)
	dbInfo.DatabaseSize = databaseSize
	return err
}
func getMySQLConnections(db *gorm.DB) (currentConnections int, maxConnections int, err error) {
	var result struct {
		VariableName string
		Value        int
	}

	// 查询当前连接数
	err = db.Raw("SHOW STATUS WHERE `variable_name` = 'Threads_connected'").Scan(&result).Error
	if err != nil {
		return 0, 0, err
	}
	currentConnections = result.Value

	// 查询最大连接数
	err = db.Raw("SHOW VARIABLES WHERE `variable_name` = 'max_connections'").Scan(&result).Error
	if err != nil {
		return 0, 0, err
	}
	maxConnections = result.Value

	return currentConnections, maxConnections, nil
}

func getMySQLDatabaseSize(db *gorm.DB, dbName string) (sizeMB string, err error) {
	query := `
        SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024) AS db_size_mb
        FROM information_schema.tables
        WHERE table_schema = ?
        GROUP BY table_schema
    `
	err = db.Raw(query, dbName).Scan(&sizeMB).Error
	return sizeMB + "MB", err
}
